#pragma once

#include <common.h>
#include <ir/ir.h>
#include <ir/genarator.h>
#include <ast/ast.h>
#include <ir/iden_stack.h>
#include <ir/array_info.h>

namespace ast {
  class NodeCompUnit;
  class NodeGlobalDecl;
  class NodeGlobalDefs;
  class NodeGlobalDef;
  class NodeConstDecl;
  class NodeConstDefs;
  class NodeConstDef;
  class NodeVarDecl;
  class NodeVarDefs;
  class NodeVarDef;
  class NodeArrayConst;
  class NodeArray;
  class NodeArrayInit;
  class NodeFuncDef;
  class NodeFuncDefArgs;
  class NodeFuncCall;
  class NodeBlock;
  class NodeBlockItems;
  class NodeStmt;
  class NodeStmtAssign;
  class NodeStmtReturn;
  class NodeStmtIf;
  class NodeStmtWhile;
  class NodeStmtBreak;
  class NodeStmtContinue;
  class NodeStmtFuncCall;
  class NodeExp;
  class NodeFuncArgs;
  class NodeCond;
}

namespace ir {
  class Visitor {
  public:
    Visitor(CompUnit * comp_unit);

    void visit(ast::NodeCompUnit & comp_unit);
    void visit(ast::NodeGlobalDecl & global_decl);
    void visit(ast::NodeGlobalDefs & global_defs);
    void visit(ast::NodeGlobalDef & global_def);
    void visit(ast::NodeConstDecl & const_decl);
    void visit(ast::NodeConstDefs & const_defs);
    void visit(ast::NodeConstDef & const_def);
    void visit(ast::NodeVarDecl & var_decl);
    void visit(ast::NodeVarDefs & var_defs);
    void visit(ast::NodeVarDef & var_def);
    void visit(ast::NodeArrayConst & array_const);
    void visit(ast::NodeArray & array);
    void visit(ast::NodeArrayInit & array_init);
    void visit(ast::NodeFuncDef & func_def);
    void visit(ast::NodeFuncDefArgs & func_def_args);
    void visit(ast::NodeFuncCall & func_call);
    void visit(ast::NodeBlock & block);
    void visit(ast::NodeBlockItems & block_items);
    void visit(ast::NodeStmt & stmt);
    void visit(ast::NodeStmtAssign & stmt_assign);
    void visit(ast::NodeStmtReturn & stmt_return);
    void visit(ast::NodeStmtIf & stmt_if);
    void visit(ast::NodeStmtWhile & stmt_while);
    void visit(ast::NodeStmtBreak & stmt_break);
    void visit(ast::NodeStmtContinue & stmt_continue);
    void visit(ast::NodeStmtFuncCall & stmt_func_call);
    void visit(ast::NodeExp & exp);
    void visit(ast::NodeFuncArgs & func_args);
    void visit(ast::NodeCond & cond);

  private:
    std::unique_ptr<Genarator> genarator;
    CompUnit * comp_unit;

    VarType current_type;
    Func * current_func;
    Block * current_cond_block;
    Block * current_block;
    // It be valid after NodeExp accept.
    Reg * current_exp_reg;
    // It should be valid befor NodeArrayInit accept.
    Reg * current_array_reg;
    ArrayInfo * current_array;  // current array
    std::vector<int> current_array_depth; // array generate information
    // Just be used in NodeArrayInit accept.
    // array generate index
    int current_array_depth_index;
    // It should be valid after NodeArray accept
    Reg * current_array_single_index_reg;
    Reg * current_func_call_reg;
    // Befor inst call, valid after NodeArgs.
    std::vector<Reg*> current_args;

    std::stack<Block *> while_begin_block;
    std::stack<Block *> while_end_block;
    IdenStack iden_stack;

    // reg src type must be i32 or f32
    // if src type not is reference type,
    // it will create reg and generate convert inst
    Reg * confirm_type(VarType reference_type, Reg * src);
    Reg * confirm_ptr_type(VarType reference_ptr_type, Reg * src);
  };
}
